package com.bird.thread.base.thread;

import java.util.Date;
import java.util.LinkedList;

/**
 * @author: bird
 * @Data: 2021/7/1 10:27
 * @description: 描述：用wait/notify 来实现生产者消费者模式
 */
public class ProducerConsumerModel {

    public static void main(String[] args) {

        EventStorage eventStorage = new EventStorage();
        Producer producer = new Producer(eventStorage);
        Consumer consumer = new Consumer(eventStorage);

        new Thread(producer).start();
        new Thread(consumer).start();

    }

}


// 生产者-生产数据 放到队列中
class Producer implements Runnable {

    private EventStorage storage;

    public Producer(EventStorage storage) {
        this.storage = storage;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            storage.put();
        }
    }
}


// 消费者
class Consumer implements Runnable {

    private EventStorage storage;

    public Consumer(EventStorage storage) {
        this.storage = storage;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            storage.take();
        }
    }
}

class EventStorage {

    // 队列的数量
    private int maxSize;

    // 存储数据
    private LinkedList<Date> storage;

    public EventStorage() {
        this.maxSize = 10;
        storage = new LinkedList<>();
    }

    // 生产者往队列中添加元素
    public synchronized void put() {
        // 如果队列满的话 停止生产
        while (storage.size() == maxSize) {
            try {
                // 把当前对象当作一把对象锁，生产者这个线程，调用了wait方法 说明生产者线程需要进入阻塞期 并释放锁 等待再次被唤醒,
                //调用wait方法之前需要先获取这个对象锁monitor.
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        storage.add(new Date());
        System.out.println("仓库里面有了 " + storage.size() + " 个产品");
        this.notify();//唤醒持有这把锁被阻塞的线程
    }

    // 消费者-从队列中取出元素
    /* 消费者-消费数据
     * 1：先判断队列中是否为空, 是 消费者就等等待
     * 2：不是的 就从队列中取出元素 然后唤醒被阻塞的线程
     *
     */
    public synchronized void take() {
        while (storage.size() == 0) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("拿到了 " + storage.poll() + "现在仓库还剩下" + storage.size());
        this.notify();// 唤醒持有这个对象锁等待的线程.
    }

    /*wait() notify() 和 obj.wait() obj.notify()的区别？
     *
     * 如果在调用wait和notify方法前面没有obj的话,那么默认是this 这个类的对象。
     *
     */
}
